﻿using System;
using System.IO;
using System.Threading;
using MediaController.Client.DAL;

namespace MediaController.Utils
{
    internal class Utils
    {
        /// <summary>
        /// Gets the base directory for this <see cref="AppDomain"/>.
        /// </summary>
        /// <value>The base directory path for the current <see cref="AppDomain"/>.</value>
        /// <remarks>
        /// <para>
        /// Gets the base directory for this <see cref="AppDomain"/>.
        /// </para>
        /// <para>
        /// The value returned may be either a local file path or a URI.
        /// </para>
        /// </remarks>
        public static string ApplicationBaseDirectory
        {
            get
            {
#if NETCF
				return System.IO.Path.GetDirectoryName(SystemInfo.EntryAssemblyLocation) + System.IO.Path.DirectorySeparatorChar;
#else
                return AppDomain.CurrentDomain.BaseDirectory;
#endif
            }
        }

        /// <summary>
        /// Convert a path into a fully qualified local file path.
        /// </summary>
        /// <param name="path">The path to convert.</param>
        /// <returns>The fully qualified path.</returns>
        /// <remarks>
        /// <para>
        /// Converts the path specified to a fully
        /// qualified path. If the path is relative it is
        /// taken as relative from the application base 
        /// directory.
        /// </para>
        /// <para>
        /// The path specified must be a local file path, a URI is not supported.
        /// </para>
        /// </remarks>
        public static string ConvertToFullPath(string path)
        {
            if (path == null)
            {
                throw new ArgumentNullException("path");
            }

            string baseDirectory = "";
            try
            {
                string applicationBaseDirectory = ApplicationBaseDirectory;
                if (applicationBaseDirectory != null)
                {
                    // applicationBaseDirectory may be a URI not a local file path
                    var applicationBaseDirectoryUri = new Uri(applicationBaseDirectory);
                    if (applicationBaseDirectoryUri.IsFile)
                    {
                        baseDirectory = applicationBaseDirectoryUri.LocalPath;
                    }
                }
            }
            catch
            {
                // Ignore URI exceptions & SecurityExceptions from SystemInfo.ApplicationBaseDirectory
            }

            if (!string.IsNullOrEmpty(baseDirectory))
            {
                // Note that Path.Combine will return the second path if it is rooted
                return Path.GetFullPath(Path.Combine(baseDirectory, path));
            }
            return Path.GetFullPath(path);
        }

       

        /// <summary>
        /// Move files from <paramref name="sourceFolder"/> to <paramref name="destFolder"/>.
        /// </summary>
        /// <remarks>
        /// The move is executed async.
        /// <para>
        /// The folder is first renamed (and new folder is created for the original name) and then a 
        /// copy process is executed so that new files will not be copied.
        /// <div>the collection of files is optimized for large amount of files</div>
        /// <div>after the copy process is complete the tmp copy folder is deleted</div>
        /// <div>if the copy process was interupted, next time it will copy old files as well because of the unique name of the copy folder</div>
        /// </para>
        /// </remarks>
        /// <param name="sourceFolder">the folder FROM with to copy the files</param>
        /// <param name="destFolder">the folder TO with to copy the files</param>
        public static void MoveFiles(string sourceFolder, string destFolder)
        {
            if (!Directory.Exists(sourceFolder)) return;

            if (!Directory.Exists(destFolder))
                Directory.CreateDirectory(destFolder);

            Logger.Log(string.Format("Move the source folder by rename so it will be easier to move files..."));
            var tmpSourceFolder = string.Format("{0}_copy_{1}_{2}", sourceFolder, DateTime.Now.ToString("ddMM"), Guid.NewGuid().ToString().Substring(0, 5));
            Directory.Move(sourceFolder, tmpSourceFolder);
            Directory.CreateDirectory(sourceFolder);

            Logger.Log(string.Format("Get the temp copy folders, include previous if found..."));
            var folderParent = new DirectoryInfo(tmpSourceFolder).Parent;
            string[] tmpCopyFolders = folderParent == null
                                           ? new[] { tmpSourceFolder }
                                           : Directory.GetDirectories(folderParent.FullName, Path.GetFileName(sourceFolder) + "_copy*");

            Logger.Log(string.Format("Start async files move for each temp folder [Count: {0}]", tmpCopyFolders.Length));
            foreach (var tmpFolderOuter in tmpCopyFolders)
            {
                string tmpFolder = tmpFolderOuter;
                ThreadPool.QueueUserWorkItem(state => MoveFilesFromFolders(tmpFolder, destFolder));
            }
        }

        /// <summary>
        /// Move all the files from <paramref name="tmpFolder"/> to <paramref name="destFolder"/>.
        /// <div>optimized for large amount of files</div>
        /// </summary>
        /// <param name="tmpFolder">the folder FROM with to copy the files</param>
        /// <param name="destFolder">the folder TO with to copy the files</param>
        private static void MoveFilesFromFolders(string tmpFolder, string destFolder)
        {
            try
            {
                Logger.Log(string.Format("Start move files... [{0}] -> [{1}]", tmpFolder, destFolder));

                var queuedFiles = FileSystemUtils.GetFiles(tmpFolder, 500);
                var queuedFolders = FileSystemUtils.GetDirectories(tmpFolder, 0, 100);
                while (queuedFiles.Count > 0 || queuedFolders.Count > 0)
                {
                    //move fils from folder
                    Logger.Log(string.Format("Start files copy [Count: {0}]", queuedFiles.Count));
                    foreach (var filePath in queuedFiles)
                    {
                        try
                        {
                            string destFilePath = Path.Combine(destFolder, Path.GetFileName(filePath));
                            File.Move(filePath, destFilePath);
                        }
                        catch (Exception ex)
                        {
                            Logger.Log(string.Format("Move file failed", ex));
                        }
                    }

                    //move subfodlers
                    Logger.Log(string.Format("Start folder copy [Count: {0}]", queuedFolders.Count));
                    foreach (var folderPath in queuedFolders)
                    {
                        try
                        {
                            string destFolderPath = Path.Combine(destFolder, Path.GetFileName(folderPath));
                            if (Directory.Exists(destFolderPath))
                            {
                                MoveFilesFromFolders(folderPath, destFolderPath);
                            }
                            else
                            {
                                Directory.Move(folderPath, destFolderPath);
                            }
                        }
                        catch (Exception ex)
                        {
                            Logger.Log(string.Format("Move file failed", ex));
                        }
                    }

                    Logger.Log(string.Format("Get next bulk of files to copy..."));
                    queuedFiles = FileSystemUtils.GetFiles(tmpFolder, 500);
                    queuedFolders = FileSystemUtils.GetDirectories(tmpFolder, 0, 100);
                }

                Directory.Delete(tmpFolder);
                Logger.Log(string.Format("Finished!"));
            }
            catch (Exception ex)
            {
                Logger.Log(string.Format(string.Format("Failed to move files [{0}] -> [{1}]: {2}", tmpFolder, destFolder, ex.Message)));
            }
        }
    }
}